/*
 * ReactionBuilder.cpp
 *
 *  Created on: 15 Jul 2011
 *      Author: al2010
 */

#include "ReactionBuilder.h"

// C++ includes
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;

// GeoReact includes
#include "Database.h"
#include "EquilibriumConstantData.h"
#include "Kinetics/MineralKinetics/MineralKineticsDatabase.h"
#include "SpeciesMap.h"
#include "Reaction.h"
#include "ReactionKinetics.h"
#include "ReactionSystem.h"

const Reaction AuxiliarCreateReaction(const SpeciesMap& speciesMap, const ReactionInfo& reactionInfo)
{
	// Create the reaction object
	Reaction reaction;
	
	// Set the attributes of the reaction and then register its index related data
	reaction.SetEquation(reactionInfo.reactant, reactionInfo.products, reactionInfo.stoichiometries);
	reaction.SetEquilibriumConstantData( EquilibriumConstantData(reactionInfo.logKi, reactionInfo.Ti) );
	reaction.SetIndexes(speciesMap);
	
	// Return the reaction object
	return reaction;
}

const ReactionSystem ReactionBuilder::CreateReactionSystem(const SpeciesMap& speciesMap, const vector<string>& species)
{
	vector<Reaction> reactions; reactions.reserve(species.size());
	
	BOOST_FOREACH(const string& s, species)
		reactions.push_back( CreateReaction(speciesMap, s) );
	
	return ReactionSystem(reactions);
}

const Reaction ReactionBuilder::CreateReaction(const SpeciesMap& speciesMap, const string& species)
{
	if(Database::GetInstance().GetSpeciesInfo(species).HasTag("basis"))
		return CreateBasisReaction(speciesMap, species);
	
	else if(Database::GetInstance().GetSpeciesInfo(species).HasTag("mineral"))
		return CreateMineralReaction(speciesMap, species);
	
	else
		return CreatePlainReaction(speciesMap, species);
}

const Reaction ReactionBuilder::CreatePlainReaction(const SpeciesMap& speciesMap, const string& species)
{
	// Get the information data of the reaction whose reactant is the given species
	const ReactionInfo reactionInfo = Database::GetInstance().GetReactionInfo(species);
	
	return AuxiliarCreateReaction(speciesMap, reactionInfo);
}

const Reaction ReactionBuilder::CreateBasisReaction(const SpeciesMap& speciesMap, const string& species)
{
	ReactionInfo reactionInfo;
	
	reactionInfo.reactant        = species;
	reactionInfo.products        = {species};
	reactionInfo.stoichiometries = {1.0};
	reactionInfo.logKi           = {0.0};  // K = 1
	reactionInfo.Ti              = {25.0}; // T = 25 degC

	return AuxiliarCreateReaction(speciesMap, reactionInfo);
}

const Reaction ReactionBuilder::CreateMineralReaction(const SpeciesMap& speciesMap, const string& mineral)
{
	// Create the reaction object for the given mineral
	Reaction mineralReaction = CreatePlainReaction(speciesMap, mineral);
	
	// Create the mineral kinetics object of the given mineral
	MineralKinetics mineralKinetics;
	
	// Look up at the database of mineral kinetics for the given mineral
	MineralKineticsInfo mineralKineticsInfo = MineralKineticsDatabase::GetInstance().GetMineralKineticsInfo(mineral);
	
	// Set the mineral kinetics object with the found information
	mineralKinetics.SetReaction(mineralReaction);
	mineralKinetics.SetSpecificSurfaceArea(mineralKineticsInfo.specificSurfaceArea);
	mineralKinetics.SetMechanisms(mineralKineticsInfo.mechanisms);
	
	// Set the kinetics of the mineral reaction
	mineralReaction.SetReactionKinetics( Reaction::ReactionKineticsPtr(new MineralKinetics(mineralKinetics)) );

	return mineralReaction;
}
